package ejbPart.entity.bankAccount;

import exceptions.AccountException;
import exceptions.LookupException;

import javax.ejb.*;
import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.naming.*;

public class AccountBean implements EntityBean {

    protected EntityContext ctx;

    /* Bean managed state fields */
    private String accountId;
    private String ownerName;
    private double balance;

    public AccountBean() {
        System.out.println("-----New bank account instance created by Container. Constructor called");
    }

         /* -------------------------- Business methods -------------------------------------- */

    /**
     * Add certain amt to the balance.
     */
    public void deposite(double amt) throws AccountException {
        System.out.println("-----Deposite method called");
        balance += amt;
    }

    /**
     * Withdraw some amount from balance.
     */
    public void withdraw(double amt) throws AccountException {
        System.out.println("-----Withdraw method called");
        if (amt > balance) {
            throw new AccountException("You balance " + balance + "is less than " + amt);
        }
        balance -= amt;
    }

        /* Getter and setter methods */

        // Getter methods of the entity bean
    public double getBalance() {
        System.out.println("getBalance() method called");
        return balance;
    }

    public String getOwnerName() {
        System.out.println("getOwnerName() method called");
        return ownerName;
    }

    public String getAccountId() {
        System.out.println("getAccountId() method called");
        return accountId;
    }

        // Setter methods of the entity bean
    public void setOwnerName(String name) {
        System.out.println("setOwnerName() called");
        ownerName = name;
    }

    public void setAccountId(String id) {
        System.out.println("setAccountId() method called");
        accountId = id;
    }

        /* ------------------------------------------------------------------------------------ */

    /**
     * This method declared in the home interface and calculate total balance in the bank.
     */
    public double ejbHomeGetTotalBankValue() throws AccountException {
        System.out.println("--------ejbHomeGetTotalBankValue() called");
        Connection connection = null;
        PreparedStatement prStm = null;
        try {
            connection = getConnection();
            final String query = "SELECT sum(balance) AS total FROM accounts";
            prStm = connection.prepareStatement(query);
            ResultSet result = prStm.executeQuery();
            if (result.next()) {
                return result.getDouble("total");
            }
        } catch (SQLException e) {
            throw new AccountException("Errors occurs during the work with database", e);
        } catch (LookupException e) {
            throw new AccountException(e);
        } finally {
            // Release resources occupied by this bean
            try {
                if (prStm != null) {
                    prStm.close();
                }
            } catch (SQLException e) { }

            try {
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) { }
        }
        throw new AccountException("Can't calculate total balance in the bank");
    }

    @Override
    public void setEntityContext(EntityContext entityContext) throws EJBException {
        ctx = entityContext;
        System.out.println("-------setEntityContext() called");
    }

    @Override
    public void unsetEntityContext() throws EJBException {
        System.out.println("-------unsetEntityContext() called");
        ctx = null;
    }

    /**
     * Removes entity bean data from database.
     */
    @Override
    public void ejbRemove() throws RemoveException, EJBException {
        System.out.println("---ejbRemove() method called");
        AccountPK pk = (AccountPK) ctx.getPrimaryKey();
        String id = pk.accountId;        // ID number of deleted row from database
        Connection connection = null;
        PreparedStatement prStm = null;
        try {
            connection = getConnection();
            final String sqlQuery = "DELETE FROM accounts WHERE id=?";
            prStm = connection.prepareStatement(sqlQuery);
            prStm.setString(1, id);
            if (prStm.executeUpdate() == 0) {
                throw new RemoveException("Account with id = " + id + "failed remove from database");
            }
        } catch (SQLException e) {
            throw new EJBException("Errors occures during the work with database", e);
        } catch (LookupException e) {
            throw new EJBException("Can't get reference to the database connections", e);
        } finally {
            // Release resources occupied by this bean
            try {
                if (prStm != null) {
                    prStm.close();
                }
            } catch (SQLException e) { }

            try {
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) { }
        }

    }

    @Override
    public void ejbActivate() throws EJBException {
        System.out.println("----Bean activated --> retrieved from pool");
    }

    @Override
    public void ejbPassivate() throws EJBException {
        System.out.println("----Bean passivated --> go to pool");
    }

    /**
     * Updates the in memory representation of database data (entity bean). Reflect the current state of the
     * database data.
     */
    @Override
    public void ejbLoad() throws EJBException {
        System.out.println("---------ejbLoad() method called");
        AccountPK primKey = (AccountPK) ctx.getPrimaryKey();
        String id = primKey.accountId;
        accountId = id;

        Connection connection = null;
        PreparedStatement prStm = null;
        try {
            connection = getConnection();
            final String sqlQuery = "SELECT ownerName, balance FROM accounts WHERE id=?";
            prStm = connection.prepareStatement(sqlQuery);
            prStm.setString(1, id);
            ResultSet result = prStm.executeQuery();
            if (result.next()) {
                ownerName = result.getString("ownerName");
                balance = result.getDouble("balance");
            }
        } catch (SQLException e) {
            throw new EJBException("Errors occures during the work with database", e);
        } catch (LookupException e) {
            throw new EJBException("Can't get reference to the database connections", e);
        } finally {
            // Release resources occupied by this bean
            try {
                if (prStm != null) {
                    prStm.close();
                }
            } catch (SQLException e) { }

            try {
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) { }
        }
    }

    @Override
    public void ejbStore() throws EJBException {
        System.out.println("-------ejbStore() called");
        Connection connection = null;
        PreparedStatement prStm = null;
        try {
            connection = getConnection();
            final String sqlQuery = "UPDATE accounts SET ownerName=?, balance=? WHERE id=?";
            prStm = connection.prepareStatement(sqlQuery);
            prStm.setString(1, ownerName);
            prStm.setDouble(2, balance);
            prStm.setString(3, accountId);

            if (prStm.executeUpdate() == 0) {
                throw new EJBException("Data that represented by id = " + accountId + " hasn't been updated");
            }
        } catch (SQLException e) {
            throw new EJBException("Errors occures during the work with database", e);
        } catch (LookupException e) {
            throw new EJBException("Can't get reference to the database connections", e);
        } finally {
            // Release resources occupied by this bean
            try {
                if (prStm != null) {
                    prStm.close();
                }
            } catch (SQLException e) { }

            try {
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) { }
        }
    }



    private Connection getConnection() throws SQLException, LookupException {
        try {
            Context context = new InitialContext();
            final String path = "java:comp/env/jdbc/ejbPool";
            DataSource ds = (DataSource) context.lookup(path);
            return ds.getConnection();
        } catch (NamingException e) {
            // Write information to log (can't lookup resource)
            throw new LookupException("Can't get reference to connection pool (lookup operation failed)", e);
        }
    }


    public void ejbPostCreate(String accountId, String ownerName) {
        System.out.println("----ejbPostCreate() called");
    }

    /**
     * Client calls create method on home object -> create new record in the database (create new instance of bean) ->
     * return primary key of created record (bean) -> container creates EJBObject instance and assign (bind) primary
     * key to that instance (possibly)
     */
    public AccountPK ejbCreate(String accountId, String ownerName) throws CreateException {
        System.out.println("------ejbCreate() called");
        System.out.println();
        Connection connection = null;
        PreparedStatement prStm = null;
        try {
            connection = getConnection();
            // Check whether the record with given id is already exists in the database
            final String checkQuery = "SELECT id FROM accounts WHERE id=?";
            prStm = connection.prepareStatement(checkQuery);
            prStm.setString(1, accountId);
            ResultSet result = prStm.executeQuery();
            // If the record with given accountId is already exists
            if (result.next()) {
                return new AccountPK(result.getString("id"));
            } else {        // Create new record
                final String createQuery = "INSERT INTO accounts (id, ownerName, balance) VALUES (?, ?, ?)";
                prStm = connection.prepareStatement(createQuery);
                prStm.setString(1, accountId);
                prStm.setString(2, ownerName);
                prStm.setDouble(3, balance);

                prStm.executeUpdate();
                return new AccountPK(accountId);
            }
        } catch (SQLException e) {
            throw new EJBException("Errors occures during the work with database", e);
        } catch (LookupException e) {
            throw new EJBException("Can't get reference to the database connections", e);
        } finally {
            // Release resources occupied by this bean
            try {
                if (prStm != null) {
                    prStm.close();
                }
            } catch (SQLException e) { }

            try {
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) { }
        }
    }


    public AccountPK ejbFindByPrimaryKey(AccountPK key) throws FinderException {
        System.out.println("-------ejbFindByPrimaryKey() called");
        Connection connection = null;
        PreparedStatement prStm = null;
        try {
            connection = getConnection();
            final String sqlQuery = "SELECT id FROM accounts WHERE id=?";
            prStm = connection.prepareStatement(sqlQuery);
            prStm.setString(1, key.accountId);
            ResultSet result = prStm.executeQuery();
            // If database have appropriate record with given ID there are no errors
            result.next();
            return key;
        } catch (SQLException e) {
            throw new EJBException("Errors occures during the work with database", e);
        } catch (LookupException e) {
            throw new EJBException("Can't get reference to the database connections", e);
        } finally {
            // Release resources occupied by this bean
            try {
                if (prStm != null) {
                    prStm.close();
                }
            } catch (SQLException e) { }

            try {
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) { }
        }

    }

    public Collection ejbFindByOwnerName(String name) throws FinderException {
        System.out.println("------ejbFindByOwnerNameCalled()");
        Connection connection = null;
        PreparedStatement prStm = null;
        List list = new ArrayList();
        try {
            connection = getConnection();
            final String sqlQuery = "SELECT id FROM accounts WHERE ownerName=?";
            prStm = connection.prepareStatement(sqlQuery);
            prStm.setString(1, name);
            ResultSet result = prStm.executeQuery();
            while (result.next()) {
                list.add(new AccountPK(result.getString("id")));
            }
            return list;
        } catch (SQLException e) {
            throw new EJBException("Errors occures during the work with database", e);
        } catch (LookupException e) {
            throw new EJBException("Can't get reference to the database connections", e);
        } finally {
            // Release resources occupied by this bean
            try {
                if (prStm != null) {
                    prStm.close();
                }
            } catch (SQLException e) { }

            try {
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) { }
        }
    }

}
